﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GraphViewer.Entity;
using System.Configuration;
using GraphViewer.Configuration;

namespace GraphViewer.Data
{
    public class DataLoader
    {
        private Data.DataEntities _data;
        private List<ProcessEntity> _processes;
        private List<GroupEntity> _groups;
        private EnvironmentConfigurationSection _environmentConfiguration;

        public List<ProcessEntity> Processes { get; set; }
        public List<GroupEntity> Groups { get; set; }

        public IEnumerable<string> LoadSystems(string server)
        {
            if (ConfigurationManager.AppSettings["DataSource"] == "Local")
            {
                _data = new Data.DataEntities(ConfigurationManager.ConnectionStrings["LocalConnection"].ConnectionString);
            }
            else
            {
                _data = new Data.DataEntities(string.Format(ConfigurationManager.ConnectionStrings["ServerConnection"].ConnectionString, server));
            }

            return from s in _data.SRC_SISTEMA
                   orderby s.NM_SISTEMA
                   select s.NM_SISTEMA;
        }

        public void LoadData(string server, string system)
        {
            if (ConfigurationManager.AppSettings["DataSource"] == "Local")
            {
                _data = new Data.DataEntities(ConfigurationManager.ConnectionStrings["LocalConnection"].ConnectionString);
            }
            else
            {
                _data = new Data.DataEntities(string.Format(ConfigurationManager.ConnectionStrings["ServerConnection"].ConnectionString, server));
            }

            _processes = new List<ProcessEntity>();
            _groups = new List<GroupEntity>();

            Processes = new List<ProcessEntity>();
            Groups = new List<GroupEntity>();

            _environmentConfiguration = EnvironmentConfigurationSection.GetSection();

            LoadGroups(system);
            LoadProcess(system);
            LoadExternalJobs(system);

            foreach (var group in _groups)
            {
                if (group.Dependencies.Count > 0)
                {
                    OrderGroups(group);
                }
            }

            foreach (var process in _processes)
            {
                if (process.Predecessors.Count > 0)
                {
                    OrderProcesses(process);
                }
            }

            PopulateGroupDependencies();
            PopulateSuccessors(system);
        }

        private void LoadGroups(string system)
        {
            var groupDB = (from p in _data.SRC_GRUPO_PROCESSO_CTM
                           join s in _data.SRC_SISTEMA
                                   on p.ID_SISTEMA equals s.ID_SISTEMA
                           join d in _data.SRC_GRUPO_DEPENDENCIA_CTM
                                   on p.ID_GRUPO_PROCESSO equals d.ID_GRUPO_PROCESSO into pd
                           from d1 in pd.DefaultIfEmpty()
                           where p.IC_ATIVO == true && s.NM_SISTEMA == system
                           orderby p.ID_GRUPO_PROCESSO
                           select new { GroupId = p.ID_GRUPO_PROCESSO, GroupName = p.NM_GRUPO_PROCESSO, PredecessorId = (d1 == null ? 0 : d1.ID_DEPENDENCIA) }).ToList();

            groupDB.Add(new { GroupId = 0, GroupName = "PREPARAR PROCESSAMENTO", PredecessorId = -1 });

            foreach (var group in groupDB)
            {
                ProcessEntity startProcessGroup = _processes.Find(p => p.ProcessId == GetDummyJobName(true, group.GroupId.ToString()));
                ProcessEntity endProcessGroup = _processes.Find(p => p.ProcessId == GetDummyJobName(false, group.GroupId.ToString()));
                GroupEntity entity = _groups.Find(p => p.GroupId == group.GroupId.ToString());

                if (entity == null)
                {
                    if (group.PredecessorId == -1)
                    {
                        _groups.Add(new GroupEntity() { GroupId = group.GroupId.ToString(), GroupName = group.GroupName });
                    }
                    else
                    {
                        _groups.Add(new GroupEntity() { GroupId = group.GroupId.ToString(), GroupName = group.GroupName, Dependencies = new List<string>() { group.PredecessorId.ToString() } });
                    }
                }
                else
                {
                    entity.Dependencies.Add(group.PredecessorId.ToString());
                }

                if (startProcessGroup == null)
                {
                    _processes.Add(new ProcessEntity() { GroupId = group.GroupId.ToString(), ProcessId = GetDummyJobName(true, group.GroupId.ToString()), JobName = GetDummyJobName(true, group.GroupId.ToString()), TaskType = "Dummy", ProcessName = group.GroupName + " - Inicio Processamento" });
                }

                if (endProcessGroup == null)
                {
                    _processes.Add(new ProcessEntity() { GroupId = group.GroupId.ToString(), ProcessId = GetDummyJobName(false, group.GroupId.ToString()), JobName = GetDummyJobName(false, group.GroupId.ToString()), TaskType = "Dummy", ProcessName = group.GroupName + " - Fim Processamento" });
                }
            }
        }

        private void LoadProcess(string system)
        {
            var processesDB = (from p in _data.SRC_PROCESSO_CTM
                               join s in _data.SRC_SISTEMA
                                   on p.ID_SISTEMA equals s.ID_SISTEMA
                               join d in _data.SRC_PROCESSO_DEPENDENCIA_CTM
                                                    on p.ID_PROCESSO equals d.ID_PROCESSO into pd
                               from r1 in pd.DefaultIfEmpty()
                               join p2 in _data.SRC_PROCESSO_CTM
                                                    on r1.ID_DEPENDENCIA equals p2.ID_PROCESSO into pd2
                               from r2 in pd2.DefaultIfEmpty()
                               where p.IC_ATIVO &&
                                     s.NM_SISTEMA == system &&
                                     (r2 == null || (r2 != null && r2.IC_ATIVO == true))
                               orderby p.ID_GRUPO_PROCESSO, p.ID_PROCESSO
                               select new { GroupId = p.ID_GRUPO_PROCESSO, ProcessId = p.ID_PROCESSO, ProcessName = p.NM_PROCESSO, JobName = p.NM_JOB, PredecessorId = (r1 == null ? -1 : r1.ID_DEPENDENCIA) }).ToList();

            foreach (var process in processesDB)
            {
                ProcessEntity entity = _processes.Find(p => p.ProcessId == process.ProcessId.ToString());

                if (entity == null)
                {
                    if (process.PredecessorId == -1)
                    {
                        _processes.Add(new ProcessEntity() { GroupId = process.GroupId.ToString(), ProcessId = process.ProcessId.ToString(), ProcessName = process.ProcessName, JobName = GetJobName(process.ProcessId.ToString()), TaskType = "Job" });
                    }
                    else
                    {
                        _processes.Add(new ProcessEntity() { GroupId = process.GroupId.ToString(), ProcessId = process.ProcessId.ToString(), ProcessName = process.ProcessName, JobName = GetJobName(process.ProcessId.ToString()), Predecessors = new List<string>() { process.PredecessorId.ToString() }, TaskType = "Job" });
                    }
                }
                else
                {
                    entity.Predecessors.Add(process.PredecessorId.ToString());
                }
            }
        }

        private void LoadExternalJobs(string system)
        {
            if (_environmentConfiguration.Systems[system] != null && _environmentConfiguration.Systems[system].Processes != null)
            {
                foreach (ProcessConfigurationElement process in _environmentConfiguration.Systems[system].Processes)
                {
                    ProcessEntity processEntity = new ProcessEntity() { GroupId = process.GroupId, ProcessId = process.ProcessId, JobName = process.Name, TaskType = process.TaskType, ProcessName = process.ProcessName, IsExternalJob = process.IsExternalJob, SourcePath = process.SourcePath, DestinationPath = process.DestinationPath };

                    if (process.Predecessors != null)
                    {
                        foreach (BaseConfigurationElement item in process.Predecessors)
                        {
                            processEntity.Predecessors.Add(item.Name);
                        }
                    }

                    if (process.Successors != null)
                    {
                        foreach (BaseConfigurationElement item in process.Successors)
                        {
                            ProcessEntity successor = _processes.Find(p => p.ProcessId == item.Name);

                            if (successor != null)
                            {
                                successor.Predecessors.Add(process.ProcessId);
                            }
                        }
                    }

                    _processes.Add(processEntity);
                }
            }

            foreach (var group in _groups)
            {
                ProcessEntity startProcessGroup = _processes.Find(p => p.ProcessId == GetDummyJobName(true, group.GroupId));
                ProcessEntity endProcessGroup = _processes.Find(p => p.ProcessId == GetDummyJobName(false, group.GroupId));

                foreach (var process in _processes.Where(p => p.GroupId == group.GroupId))
                {
                    if (process.Predecessors.Count == 0 && process.ProcessId != startProcessGroup.ProcessId && process.ProcessId != endProcessGroup.ProcessId)
                    {
                        process.Predecessors.Add(startProcessGroup.ProcessId);
                    }

                    if (!_processes.Exists(p => p.Predecessors.Exists(d => d == process.ProcessId)) && process.ProcessId != startProcessGroup.ProcessId && process.ProcessId != endProcessGroup.ProcessId)
                    {
                        endProcessGroup.Predecessors.Add(process.ProcessId);
                    }
                }
            }
        }

        private void OrderGroups(GroupEntity entity)
        {
            foreach (string predecessor in entity.Dependencies)
            {
                GroupEntity groupDependency = _groups.Find(p => p.GroupId == predecessor);

                if (groupDependency != null && entity.Dependencies.Count > 0)
                {
                    OrderGroups(groupDependency);
                }
            }

            if (Groups.Find(p => p.GroupId == entity.GroupId) == null)
            {
                Groups.Add(entity);
            }
        }

        private void OrderProcesses(ProcessEntity entity)
        {
            foreach (string predecessor in entity.Predecessors)
            {
                ProcessEntity processDependency = _processes.Find(p => p.ProcessId == predecessor);

                if (processDependency != null && entity.Predecessors.Count > 0)
                {
                    OrderProcesses(processDependency);
                }
            }

            if (Processes.Find(p => p.ProcessId == entity.ProcessId) == null)
            {
                Processes.Add(entity);
            }
        }

        private void PopulateGroupDependencies()
        {
            foreach (GroupEntity group in Groups)
            {
                foreach (var groupDependendy in group.Dependencies)
                {
                    var process = Processes.Find(p => p.ProcessId == GetDummyJobName(true, group.GroupId));

                    if (process != null)
                    {
                        process.Predecessors.Add(GetDummyJobName(false, groupDependendy));
                    }
                }
            }
        }

        private void PopulateSuccessors(string system)
        {
            foreach (var process in Processes)
            {
                process.Successors = (from p in Processes
                                      from d in p.Predecessors
                                      where d == process.ProcessId
                                      select p.ProcessId).Distinct().ToList();
            }

            if (_environmentConfiguration.Systems[system] != null && _environmentConfiguration.Systems[system].ExternalDependencies != null)
            {
                foreach (DependencyConfigurationElement d in _environmentConfiguration.Systems[system].ExternalDependencies)
                {
                    var process = _processes.Find(p => p.ProcessId == d.Dependency);

                    if (process != null)
                    {
                        process.Successors.Add(d.Name);
                    }
                }
            }
        }

        public string GetJobName(string processId)
        {
            int p = 0;
            string jobname = null;

            if (int.TryParse(processId, out p))
            {
                jobname = "RSCIS" + (p + 100).ToString("X").PadLeft(3, '0') + "DP";
            }

            return jobname;
        }

        public string GetDummyJobName(bool startJob, string groupId)
        {
            if (startJob)
            {
                return string.Format("RSCIS{0}{1}DP", "I", groupId.PadLeft(2, '0'));
            }
            else
            {
                if (groupId.Length == 1)
                    return string.Format("RSCISDM{0}DP", groupId);
                else
                    return string.Format("RSCISD{0}DP", groupId);
            }
        }
    }
}
